/* 
 * File:   main.cpp
 * Author: ungerma
 *
 * Created on 04. August 2014, 14:12
 */

/*
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * 
 * To be able to run Dual Process Model experiments, you have to
 * compile your own version of SVM Light with DPM-kernels.
 * 
 * Refer to kernel.h in the bin-directory for further details.
 * 
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */

#include <iostream>
#include <exception>
#include <stdexcept>

#include "opencv2/highgui/highgui.hpp"

#include "Images.h"
#include "SupportVectorMachine.h"

using namespace std;
using namespace cv;

void usage (string call) {
    
    cout << "DPM pedestrian detection" << endl << endl;
    cout << "Usage: " << call << " [mode [svm-light-params]]" << endl << endl;
    
    cout << "Possible values for mode:" << endl;
    cout << "\tfeatures:\tCalculate and store feature vectors" << endl;
    cout << "\ttrain:\t\tTrain SVM" << endl;
    cout << "\tclassify:\tClassify test images" << endl;
    cout << "If no mode is given, we perform all modes in succession" << endl 
            << endl;
    
    cout << "svm-light-params: Can be any string. Passed to svm_learn" << endl;
    cout << "\tUse with custom-built svm_learn and svm_classify for DPM" 
            << endl;
    cout << "\tIf nothing is given, we use a linear kernel" << endl;
    cout << "\tTry \"-t 4 -g 2 -s 1 -d 0 -r 1\" for a good DPM"
            << endl;
}

/*
 * Main Entry point for DPM pedestrian detection.
 */
int main(int argc, char** argv) {
   
    const string TRAIN_POS    = ".\\pos\\";
    const string TRAIN_NEG    = ".\\neg\\";
    const string TEST_POS     = ".\\test\\pos\\";
    const string TEST_NEG     = ".\\test\\neg\\";
    const string TEST_FILE    = ".\\gen\\test.txt";
    
    const string FEATURE_FILE     = ".\\gen\\features.txt";
    const string SCD_FILE         = ".\\gen\\scd.txt";
    const string EHD_FILE         = ".\\gen\\ehd.txt";
    const string MPEG7_TRAIN_LIST = ".\\gen\\imtrain.txt";
    const string MPEG7_TEST_LIST  = ".\\gen\\imtest.txt";
    const string MPEG7_GEN        = ".\\bin\\MPEG7Fex.exe";
    const string SVM_LEARN        = ".\\bin\\svm_learn.exe";
    const string SVM_CLASSIFY     = ".\\bin\\svm_classify.exe";
    const string SVM_FILE         = ".\\gen\\svm.txt";
    const string CLZ_FILE         = ".\\gen\\clz.txt";
    const string NAME_FILE        = ".\\gen\\clz-names.txt";
    const string PR_FILE_PRE      = ".\\gen\\pr";
    const string PR_FILE_POST     = ".txt";

    HOGSettings settings = HOGSettings();
    Images images = Images(settings);
    string svmParameters("");
    SupportVectorMachine svm = SupportVectorMachine(settings);
    
    int dirty = 0;
    string mode(""), call(argv[0]);
    
    if(argc > 1) {
        mode = string(argv[1]);
    }   
    
    if(argc > 3) {
        usage(call);
        return EXIT_FAILURE;
    }
    
    try {
        
        if(argc > 2) {
            svmParameters = argv[2];
        }
        
        if(mode == "features" || mode.empty()) {
            
            cout << "Generating feature vectors for training" << endl; 
            ++ dirty;
            images.readTrain(TRAIN_POS, TRAIN_NEG, FEATURE_FILE, 
                    MPEG7_TRAIN_LIST, MPEG7_GEN, SCD_FILE, EHD_FILE);            
            
        }
        
        if(mode == "train" || mode.empty()) {
            
            cout << "Training support vector machine" << endl; 
            ++ dirty;
            svm.train(SVM_LEARN, FEATURE_FILE, SVM_FILE, svmParameters);
            
        }
        
        if(mode == "classify" || mode.empty()) {
            
            cout << "Running classification" << endl; 
            ++ dirty;
            images.readTest(TEST_POS, TEST_NEG, TEST_FILE, MPEG7_TEST_LIST,
                    MPEG7_GEN, SCD_FILE, EHD_FILE, NAME_FILE);             
            TestResults results = 
                    svm.classify(SVM_CLASSIFY, TEST_FILE, SVM_FILE, CLZ_FILE, 
                    images);
            results.summaryOutput();
            results.precisionRecallCurve(PR_FILE_PRE + results.okString() + 
            PR_FILE_POST);
            
        }
        
    }
    catch(exception& e) {
        
        cout << "Programm error: " << e.what() << endl;
        return EXIT_FAILURE;
        
    }
    
    if(dirty == 0) {
        
        usage(call);
        return EXIT_FAILURE;
        
    }
    
    return EXIT_SUCCESS;
    
}
